﻿#include "specificattrpaneladapter.h"
#include <QSignalBlocker>
#include <model/datamanager.h>


/**
 * @brief SpecificAttrPanelAdapter::SpecificAttrPanelAdapter
 * @details 构造特有属性面板适配器并注入全部依赖控件。
 *          构造期间会执行以下步骤：
 *          1. 依据 DeviceRole 将各页面加入堆栈并建立映射；
 *          2. 将堆栈挂载到滚动容器，如无布局则自动创建；
 *          3. 主动刷新参数表下拉框，保持初始数据一致；
 *          4. 绑定控件信号，统一通过 specificEdited 派发。
 * @param scrollAreaContainer 包裹堆栈的容器。
 * @param stack 存放各角色页面的堆栈控件。
 * @param wSignalGenerator 信号发生器页面。
 * @param sgMin 信号源最小值输入框。
 * @param sgMax 信号源最大值输入框。
 * @param wAmplifier 功放页面。
 * @param paInputMax 功放输入上限。
 * @param paCplConstRadio 功放耦合常数单选。
 * @param paCplConstEdit 功放耦合常数输入框。
 * @param paCplTableRadio 功放耦合表单选。
 * @param paFwdTable 功放正向耦合表选择。
 * @param paRevTable 功放反向耦合表选择。
 * @param wPowerMeter 功率计页面。
 * @param pmChannel 功率计通道选择。
 * @param wAntenna 天线页面。
 * @param antH 水平增益表选择。
 * @param antV 垂直增益表选择。
 * @param wConnector 连接器页面。
 * @param conConstRadio 连接器常数单选。
 * @param conConstEdit 连接器常数输入框。
 * @param conTableRadio 连接器表单选。
 * @param conTableBox 连接器表选择。
 * @param switchWidget 开关设置区域。
 * @param switchSel 开关路径选择。
 * @param switchCh 开关通道选择。
 * @param wFieldProbe 场探头页面。
 * @param fpCorr 场探头修正表选择。
 * @param wCurrentProbe 电流探头页面。
 * @param cpTransducer 电流探头换能器选择。
 * @param wReceiver 接收机空页面。
 * @param parent Qt 父对象。
 */
SpecificAttrPanelAdapter::SpecificAttrPanelAdapter(
    QWidget *scrollAreaContainer, QStackedWidget *stack,
    QWidget *wSignalGenerator, QLineEdit *sgMin, QLineEdit *sgMax,
    QWidget *wAmplifier, QLineEdit *paInputMax,
    QRadioButton *paCplConstRadio, QLineEdit *paCplConstEdit,
    QRadioButton *paCplTableRadio, QComboBox *paFwdTable, QComboBox *paRevTable,
    QWidget *wPowerMeter, QComboBox *pmChannel,
    QWidget *wAntenna, QComboBox *antH, QComboBox *antV,
    QWidget *wConnector, QRadioButton *conConstRadio, QLineEdit *conConstEdit,
    QRadioButton *conTableRadio, QComboBox *conTableBox,
    QWidget *switchWidget, QComboBox *switchSel, QComboBox *switchCh,
    QWidget *wFieldProbe, QComboBox *fpCorr,
    QWidget *wCurrentProbe, QComboBox *cpTransducer,
    QWidget *wReceiver,
    QObject *parent)
    : QObject(parent)
    , container_(scrollAreaContainer)
    , stack_(stack)
    // SG
    , wSignalGenerator_(wSignalGenerator)
    , sgMin_(sgMin)
    , sgMax_(sgMax)
    // Amp
    , wAmplifier_(wAmplifier)
    , paInputMax_(paInputMax)
    , paCplConstRadio_(paCplConstRadio)
    , paCplConstEdit_(paCplConstEdit)
    , paCplTableRadio_(paCplTableRadio)
    , paFwdTable_(paFwdTable)
    , paRevTable_(paRevTable)
    // PM
    , wPowerMeter_(wPowerMeter)
    , pmChannel_(pmChannel)
    // Ant
    , wAntenna_(wAntenna)
    , antH_(antH)
    , antV_(antV)
    // Con
    , wConnector_(wConnector)
    , conConstRadio_(conConstRadio)
    , conConstEdit_(conConstEdit)
    , conTableRadio_(conTableRadio)
    , conTableBox_(conTableBox)
    , switchWidget_(switchWidget)
    , switchSel_(switchSel)
    , switchCh_(switchCh)
    // FP
    , wFieldProbe_(wFieldProbe)
    , fpCorr_(fpCorr)
    // CP
    , wCurrentProbe_(wCurrentProbe)
    , cpTransducer_(cpTransducer)
    // RX
    , wReceiver_(wReceiver)
{
    // 栈装页
    roleToPage_[DeviceRole::SignalGenerator] = addPage(wSignalGenerator_);
    roleToPage_[DeviceRole::Amplifier] = addPage(wAmplifier_);
    roleToPage_[DeviceRole::PowerMeterForward] = addPage(wPowerMeter_);
    roleToPage_[DeviceRole::PowerMeterReverse] = roleToPage_[DeviceRole::PowerMeterForward];
    roleToPage_[DeviceRole::PowerMeterMonitor] = roleToPage_[DeviceRole::PowerMeterForward];
    roleToPage_[DeviceRole::AntennaReceives] = addPage(wAntenna_);
    roleToPage_[DeviceRole::FieldProbe] = addPage(wFieldProbe_);
    roleToPage_[DeviceRole::Connector_SIG2PA] = addPage(wConnector_);
    roleToPage_[DeviceRole::Connector_FWD2PM] = roleToPage_[DeviceRole::Connector_SIG2PA];
    roleToPage_[DeviceRole::Connector_REV2PM] = roleToPage_[DeviceRole::Connector_SIG2PA];
    roleToPage_[DeviceRole::Connector_PA2Transducers] = roleToPage_[DeviceRole::Connector_SIG2PA];
    roleToPage_[DeviceRole::Connector_Transducers2PM] = roleToPage_[DeviceRole::Connector_SIG2PA];
    roleToPage_[DeviceRole::CurrentInjectionProbeInput] = addPage(wCurrentProbe_);
    roleToPage_[DeviceRole::CurrentInjectionProbeMonitor] = roleToPage_[DeviceRole::CurrentInjectionProbeInput];
    roleToPage_[DeviceRole::Receiver] = addPage(wReceiver_);

    if (container_) {
        if (!container_->layout()) {
            auto *v = new QVBoxLayout(container_);
            v->setContentsMargins(0, 0, 0, 0);
            v->addWidget(stack_);
        } else {
            container_->layout()->addWidget(stack_);
        }
    }

    // 初始化选项内容（各表格）
    refreshParameterTables();
    connect(DataManager::instance(), &DataManager::sig_parameterTablesChanged,
            this, &SpecificAttrPanelAdapter::refreshParameterTables);

    // 绑定信号（用户编辑 -> 发射统一信号）
    wireUpSignals();
}

/**
 * @brief SpecificAttrPanelAdapter::showRole
 * @details 切换堆栈页面到指定角色，并记录当前激活角色，方便 specificEdited 引用。
 * @param role 目标设备角色。
 */
void SpecificAttrPanelAdapter::showRole(DeviceRole role)
{
    currentRole_ = role;
    int page = roleToPage_.value(role, -1);
    if (page >= 0)
        stack_->setCurrentIndex(page);
}

// 从文档读数据灌入控件
/**
 * @brief SpecificAttrPanelAdapter::loadFromDoc
 * @details 将模板文档中的特有属性写入控件。通过 QSignalBlocker 防止回写信号，
 *          并按角色类型分别解析字段，同时维持下拉框的名称-ID 对应关系。
 * @param doc 模板数据指针。
 * @param role 当前设备角色。
 */
void SpecificAttrPanelAdapter::loadFromDoc(const HardwareTemplatePtr &doc, DeviceRole role)
{
    if (!doc)
        return;
    QSignalBlocker b1(sgMin_);
    QSignalBlocker b2(sgMax_);
    QSignalBlocker b3(paInputMax_);
    QSignalBlocker b4(paCplConstEdit_);
    QSignalBlocker b5(conConstEdit_);

    // 根据角色种类分别加载不同字段，保持界面与文档同步
    switch (role) {
    case DeviceRole::SignalGenerator:
        if (doc->d_Generator) {
            sgMin_->setText(QString::number(doc->d_Generator->LevelMin));
            sgMax_->setText(QString::number(doc->d_Generator->LevelMax));
        }
        break;
    case DeviceRole::Amplifier:
        if (doc->d_Amplifier) {
            paInputMax_->setText(QString::number(doc->d_Amplifier->InputLevelMax));
            paCplConstEdit_->setText(QString::number(doc->d_Amplifier->CouplerValue));
            paCplConstRadio_->setChecked(doc->d_Amplifier->CouplerType == 0);
            paCplTableRadio_->setChecked(doc->d_Amplifier->CouplerType == 1);
            setComboByParamId(paFwdTable_, ParamTableKind::Attenuation, doc->d_Amplifier->CouplerTableFWDTableID);
            setComboByParamId(paRevTable_, ParamTableKind::Attenuation, doc->d_Amplifier->CouplerTableREVTableID);
        }
        break;
    case DeviceRole::PowerMeterForward:
    case DeviceRole::PowerMeterReverse:
    case DeviceRole::PowerMeterMonitor:
        if (auto pm = (role == DeviceRole::PowerMeterForward ? doc->d_FwdPowerMeter : role == DeviceRole::PowerMeterReverse ? doc->d_RevPowerMeter
                                                                                                                            : doc->d_MonPowerMeter)) {
            pmChannel_->setCurrentText(pm->DeviceChannel);
        }
        break;
    case DeviceRole::AntennaReceives:
        if (doc->d_ReceivesAntenna) {
            setComboByParamId(antH_, ParamTableKind::Transducer, doc->d_ReceivesAntenna->HFactorsTableID);
            setComboByParamId(antV_, ParamTableKind::Transducer, doc->d_ReceivesAntenna->VFactorsTableID);
        }
        break;
    case DeviceRole::FieldProbe:
        if (doc->d_MonFieldProbes) {
            setComboByParamId(fpCorr_, ParamTableKind::Attenuation, doc->d_MonFieldProbes->CorrectionXYZTableID);
        }
        break;
    case DeviceRole::Connector_SIG2PA:
    case DeviceRole::Connector_FWD2PM:
    case DeviceRole::Connector_REV2PM:
    case DeviceRole::Connector_PA2Transducers:
    case DeviceRole::Connector_Transducers2PM:
        if (auto c = connectorForRole(doc, role)) {
            conConstRadio_->setChecked(c->ValueType == 0);
            conTableRadio_->setChecked(c->ValueType == 1);
            conConstEdit_->setText(QString::number(c->AttValue));
            setComboByParamId(conTableBox_, ParamTableKind::Attenuation, c->AttTableID);
            // 非“无设备”型号才需要显示开关相关配置
            bool isSw = (c->gAttributes.Model == STR_NO_DEVICE) ? false : true;
            switchWidget_->setVisible(isSw);
            switchSel_->setCurrentText(QString::number(c->switchPath));
            switchCh_->setCurrentText(QString::number(c->switchChannel));
        }
        break;
    case DeviceRole::CurrentInjectionProbeInput:
    case DeviceRole::CurrentInjectionProbeMonitor:
        if (auto cp = (role == DeviceRole::CurrentInjectionProbeInput ? doc->d_InjectionTransducer : doc->d_MonitoringTransducer)) {
            setComboByParamId(cpTransducer_, ParamTableKind::Transducer, cp->TransducerTableID);
        }
        break;
    case DeviceRole::Receiver:
    default:
        break;
    }
}

/**
 * @brief SpecificAttrPanelAdapter::refreshParameterTables
 * @details 从数据管理器重新拉取参数表概览，并逐个刷新相关下拉框。
 *          在刷新过程中保留原选项，避免用户已选内容被重置。
 * @return 无返回值。
 */
void SpecificAttrPanelAdapter::refreshParameterTables()
{
    // 刷新各表格下拉（不触发回环）
    auto ovs = DataManager::instance()->getParameterTableOverviews();

    auto fill = [&](QComboBox *box, ParamTableKind kind) {
        if (!box)
            return;
        QSignalBlocker b(box);
        QString keep = box->currentText();
        box->clear();
        box->addItem(STR_NO_TABLE);
        for (const auto &it : ovs) {
            if (it.type == kind)
                box->addItem(it.name);
        }
        box->setCurrentText(keep);
    };

    fill(paFwdTable_, ParamTableKind::Attenuation);
    fill(paRevTable_, ParamTableKind::Attenuation);
    fill(conTableBox_, ParamTableKind::Attenuation);
    fill(fpCorr_, ParamTableKind::Attenuation);

    fill(antH_, ParamTableKind::Transducer);
    fill(antV_, ParamTableKind::Transducer);
    fill(cpTransducer_, ParamTableKind::Transducer);
}

/**
 * @brief SpecificAttrPanelAdapter::addPage
 * @details 将页面添加到堆栈，如果提供空指针则创建一个空白页占位，确保映射完整。
 * @param w 待添加页面。
 * @return 添加后页面索引。
 */
int SpecificAttrPanelAdapter::addPage(QWidget *w)
{
    if (!w) {
        // 放一个空白页
        QWidget *blank = new QWidget(stack_);
        return stack_->addWidget(blank);
    }
    return stack_->addWidget(w);
}

/**
 * @brief SpecificAttrPanelAdapter::setComboByParamId
 * @details 根据参数表 ID 定位名称并更新下拉框显示；若名称缺失则回退为“无表”。
 * @param cb 目标下拉框。
 * @param kind 参数表类型。
 * @param id 参数表 ID。
 * @return 无返回值。
 */
void SpecificAttrPanelAdapter::setComboByParamId(QComboBox *cb, ParamTableKind kind, int id)
{
    if (!cb)
        return;
    QString name = DataManager::instance()->findNameByOverViewID(
        DataManager::instance()->getParameterTableOverviews(), kind, id);
    QSignalBlocker b(cb);
    cb->setCurrentText(name.isEmpty() ? STR_NO_TABLE : name);
}

/**
 * @brief SpecificAttrPanelAdapter::connectorForRole
 * @details 根据设备角色返回对应的连接器配置。
 * @param doc 硬件模板。
 * @param role 当前设备角色。
 * @return 匹配的连接器配置；若不存在则返回空指针。
 */
ConnectorConfigPtr SpecificAttrPanelAdapter::connectorForRole(const HardwareTemplatePtr &doc, DeviceRole role) const
{
    switch (role) {
    case DeviceRole::Connector_SIG2PA:
        return doc->c_SIG2PA;
    case DeviceRole::Connector_FWD2PM:
        return doc->c_FWD2PM;
    case DeviceRole::Connector_REV2PM:
        return doc->c_REV2PM;
    case DeviceRole::Connector_PA2Transducers:
        return doc->c_PA2Transducers;
    case DeviceRole::Connector_Transducers2PM:
        return doc->c_Transducers2PM;
    default:
        return ConnectorConfigPtr();
    }
}

/**
 * @brief SpecificAttrPanelAdapter::wireUpSignals
 * @details 建立所有控件的信号连接，使界面修改统一通过 specificEdited 上报，
 *          形成 Presenter 的单一入口。
 */
void SpecificAttrPanelAdapter::wireUpSignals()
{
    // SG
    connect(sgMin_, &QLineEdit::editingFinished, this, [this] {
        emit specificEdited(currentRole_, "SG.LevelMin", sgMin_->text().toDouble());
    });
    connect(sgMax_, &QLineEdit::editingFinished, this, [this] {
        emit specificEdited(currentRole_, "SG.LevelMax", sgMax_->text().toDouble());
    });

    // Amp
    connect(paInputMax_, &QLineEdit::editingFinished, this, [this] {
        emit specificEdited(currentRole_, "PA.InputLevelMax", paInputMax_->text().toDouble());
    });
    connect(paCplConstEdit_, &QLineEdit::editingFinished, this, [this] {
        emit specificEdited(currentRole_, "PA.CouplerValue", paCplConstEdit_->text().toDouble());
    });
    connect(paCplConstRadio_, &QRadioButton::toggled, this, [this](bool on) {
        if (on)
            emit specificEdited(currentRole_, "PA.CouplerType", 0);
    });
    connect(paCplTableRadio_, &QRadioButton::toggled, this, [this](bool on) {
        if (on)
            emit specificEdited(currentRole_, "PA.CouplerType", 1);
    });
    connect(paFwdTable_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Attenuation, name);
        emit specificEdited(currentRole_, "PA.CouplerTableFWDTableID", id);
    });
    connect(paRevTable_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Attenuation, name);
        emit specificEdited(currentRole_, "PA.CouplerTableREVTableID", id);
    });

    // PM
    connect(pmChannel_, &QComboBox::currentTextChanged, this, [this](const QString &ch) {
        emit specificEdited(currentRole_, "PM.Channel", ch);
    });

    // Ant
    connect(antH_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Transducer, name);
        emit specificEdited(currentRole_, "ANT.HFactorsTableID", id);
    });
    connect(antV_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Transducer, name);
        emit specificEdited(currentRole_, "ANT.VFactorsTableID", id);
    });

    // Connector
    connect(conConstRadio_, &QRadioButton::toggled, this, [this](bool on) {
        if (on)
            emit specificEdited(currentRole_, "CON.ValueType", 0);
    });
    connect(conTableRadio_, &QRadioButton::toggled, this, [this](bool on) {
        if (on)
            emit specificEdited(currentRole_, "CON.ValueType", 1);
    });
    connect(conConstEdit_, &QLineEdit::editingFinished, this, [this] {
        emit specificEdited(currentRole_, "CON.AttValue", conConstEdit_->text().toDouble());
    });
    connect(conTableBox_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Attenuation, name);
        emit specificEdited(currentRole_, "CON.AttTableID", id);
    });
    connect(switchSel_, &QComboBox::currentTextChanged, this, [this](const QString &s) {
        emit specificEdited(currentRole_, "CON.switchPath", s.toInt());
    });
    connect(switchCh_, &QComboBox::currentTextChanged, this, [this](const QString &s) {
        emit specificEdited(currentRole_, "CON.switchChannel", s.toInt());
    });

    // FieldProbe
    connect(fpCorr_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Attenuation, name);
        emit specificEdited(currentRole_, "FP.CorrectionXYZTableID", id);
    });

    // CurrentProbe
    connect(cpTransducer_, &QComboBox::currentTextChanged, this, [this](const QString &name) {
        int id = DataManager::instance()->findIDByOverViewName(
            DataManager::instance()->getParameterTableOverviews(),
            ParamTableKind::Transducer, name);
        emit specificEdited(currentRole_, "CP.TransducerTableID", id);
    });
}
